from django.db.models.constants import LOOKUP_SEP
from rest_framework.compat import distinct
from rest_framework.filters import SearchFilter, OrderingFilter
from django.db import models


class SearchField:

    def __init__(self, search_field=None, foreign_key_field=None, connect='AND', lookup='icontains'):
        self.search_field = search_field
        self.foreign_key_field = foreign_key_field
        self.connect = connect
        self.lookup = lookup

    def __eq__(self, other):
        return self.search_field == other.search_field

    def __str__(self):
        return f'SearchField<search_field={self.search_field}, foreign_key_field={self.foreign_key_field}, connect={self.connect}>'

    def __repr__(self):
        return f'SearchField<search_field={self.search_field}, foreign_key_field={self.foreign_key_field}, connect={self.connect}>'


class MSearchFilter(SearchFilter):
    def __init__(self, search_param=None):
        if search_param is not None:
            self.search_param = search_param

    def get_search_terms(self, request):
        """
        Search terms are set by a ?search=... query parameter,
        and may be comma and/or whitespace delimited.
        """
        search_terms = dict(request.query_params)
        for k, v in search_terms.items():
            params = v[0]
            params = params.replace('\x00', '')  # 去掉带空字符
            params = params.replace(',', ' ')
            search_terms[k] = params.split()
        return search_terms

    def construct_search(self, field: SearchField):
        lookup = self.lookup_prefixes.get(field.search_field)
        if lookup:
            field = field[1:]
        elif field.foreign_key_field:
            lookup = f'{field.foreign_key_field}__{field.lookup}'
        else:
            lookup = field.lookup
        return LOOKUP_SEP.join([field.search_field, lookup])

    def filter_queryset(self, request, queryset, view):
        search_fields = self.get_search_fields(view, request)
        search_terms = self.get_search_terms(request)

        if not search_fields or not search_terms:
            return queryset

        orm_lookups = []
        # ['id__it', 'type__id__it']
        for search_field in search_fields:
            orm_lookups.append(self.construct_search(search_field))
        base = queryset
        conditions = None
        # {'id': ['1', '2'], 'type': ['2']}
        # param = orm_lookup[0:orm_lookup.find('_')]
        for key, value in search_terms.items():
            index = 0
            for orm_lookup in orm_lookups:
                if not orm_lookup.startswith(key):
                    index += 1
                    continue
                queries = None
                for item in value:
                    # queries = [
                    #     models.Q(**{orm_lookup: value[0]})
                    #     for orm_lookup in orm_lookups
                    # ]
                    _q = models.Q(**{orm_lookup: item})
                    queries = queries | _q if queries else _q
                if queries:
                    search_field = search_fields[index]
                    if search_field.connect == 'AND':
                        conditions = conditions & queries if conditions else queries
                    elif search_field.connect == 'OR':
                        conditions = conditions | queries if conditions else queries
                index += 1
        if conditions:
            queryset = queryset.filter(conditions)

        _search_fields = []
        for search_field in search_fields:
            _search_fields.append(search_field.search_field)
        search_fields = tuple(_search_fields)

        if self.must_call_distinct(queryset, search_fields):
            # Filtering against a many-to-many field requires us to
            # call queryset.distinct() in order to avoid duplicate items
            # in the resulting queryset.
            # We try to avoid this if possible, for performance reasons.
            queryset = distinct(queryset, base)
        return queryset


class MOrderingFilter(OrderingFilter):
    pass
    # def remove_invalid_fields(self, queryset, fields, view, request):
    #     valid_fields = [item[0] for item in self.get_valid_fields(queryset, view, {'request': request})]
    #
    #     def term_valid(term):
    #         if term.startswith("-"):
    #             term = term[1:]
    #         return term in fields
    #
    #     return [term for term in valid_fields if term_valid(term)]
